{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Running a Power Flow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We load the simple example network from the create_network tutorial from the pandapower.networks module:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pandapower.networks import example_simple\n",
    "from pandapower.run import runpp, set_user_pf_options"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net = example_simple()\n",
    "net"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run a Power Flow and Access Results"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Runing a loadflow adds seperate result table with the prefix 'res_':"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These results tables are pandas datafarmes with the same index as the element table. For example, the bus table contains all bus voltages and summed bus power injections:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_bus"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now use pandas functionality to analyse the loadflow results, for example to get the minimum voltage in the medium voltage level:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_bus[net.bus.vn_kv==20.].vm_pu.min()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "or the maxium voltage at a bus with load or generation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "load_or_generation_buses = set(net.load.bus.values) | set(net.sgen.bus.values) | set(net.gen.bus.values)\n",
    "net.res_bus.vm_pu.loc[list(load_or_generation_buses)].max()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For more on how to use pandas for data analysis in pandapower, see the tutorial on [data analysis](data_analysis.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Result tables"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Each element (except the switch) has its own result table with results tailored to the specific element. Here, we just show each table. For parameters definitions, see the documentation of the datastructure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_bus"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_ext_grid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_line"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_trafo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_load"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_sgen"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_gen"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_shunt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Voltage Angles and Initialization"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Maybe you wondered why even though there is a voltage angle of 50 degrees defined for the external grid: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.ext_grid.va_degree"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "and a shift of 150° over the HV/MV transformer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.trafo.shift_degree"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "the voltage angles are all close to zero:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net)\n",
    "net.res_bus.va_degree"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That is because the standard parameter for calculate_voltage_angles is False, which means voltage angles at external grids and transformer shifts are ignored by default. In a radial network, the absolute voltage angle shifts do not have an influence on the power flow, which is why they are disabled by default. In meshed networks however, where multiple external grids are galvanically coupled, it is always necessary to calculate the voltage angles.\n",
    "\n",
    "Suppose we want to calculate the correct voltage angles and set calculate_voltage_angles to True:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, calculate_voltage_angles=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now the power flow does not converge. This can happen with large angle shifts. The solution is to use a initialization with a DC loadflow instead of a flat start, which is default behaviour:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, calculate_voltage_angles=True, init=\"dc\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we can see that all voltage angles are correctly calculated:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.res_bus.va_degree"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we already have a solution, we can also initialize the loadflow with the voltage values from the last loadflow:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, calculate_voltage_angles=True, init=\"results\")\n",
    "net.res_bus.va_degree"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The power flow converges and yields correct results where a flat start power flow would have failed.\n",
    "\n",
    "Initializing with previous results can save convergence time in cases where multiple power flows with simliar input parameters are carried out consecutively, such as in quasi-static time series simulations."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transformer Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The parameter \"trafo_model\" can be used to switch between a 'pi' and a 't' transformer model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, trafo_model=\"t\")\n",
    "net.res_trafo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, trafo_model=\"pi\")\n",
    "net.res_trafo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For a definition of the different transformer model see the power flow model documentation of the transformer element."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transformer Loading"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The transformer loading can either be calculated in relation to the rated current:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, trafo_loading=\"current\")\n",
    "net.res_trafo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "or to the rated power of the transformer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, trafo_loading=\"power\")\n",
    "net.res_trafo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The transformer loading does not have an influence on other power flow results besides the loading_percent parameter."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generator Reactive Power Limits"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The generator has reactive power limits of -3/+3 Mvar:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "net.gen"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "which are however exceeded in the power flow results, because the enforce_q_lims option defaults to False:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net)\n",
    "net.res_gen"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If the enforce_q_lims parameter is set to True, the reactive power limit is complied with, while the voltage deviates from the voltage set point of the generator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, enforce_q_lims=True)\n",
    "net.res_gen"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you want to know what to do when a power flow does not converge, continue with the [diagnostic tutorial](diagnostic.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Changing the Power Flow Algorithm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are 5 algorithms available for solving the power flow problem:\n",
    "* \"nr\" **Newton-Raphson** - default algorithm option\n",
    "* \"bfsw\" **Backward/Forward Sweep** (specially suited for radial and weakly-meshed networks)\n",
    "* \"gs\" **Gauss-Seidel** (pypower implementation)\n",
    "* \"fdbx\" **Fast-Decoupled**  power flow using XB method (pypower implementation)\n",
    "* \"fdxb\" **Fast-Decoupled**  power flow using BX method (pypower implementation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Each algorithm can be selected by passing corresponding string {\"nr\", \"bfsw\", \"gs\", \"fdbx\", \"fdxb\"} to the parameter algorithm.  \n",
    "For example, if you want to use the **Backward/Forward sweep** algorithm:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, algorithm=\"bfsw\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or in the case of **Gauss-Seidel**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, algorithm=\"gs\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If power flow is run without setting the algorithm parameter, **Newton-Raphson** will be used as the default algorithm option."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There is also possibility to select **maximum number of iterations** that will be used for the specific algorithm.  \n",
    "In the following example max_iteration is set to 10, which is obviously not enough for the Gauss-Seidel to converge:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "raises-exception"
    ]
   },
   "outputs": [],
   "source": [
    "runpp(net, algorithm=\"gs\", max_iteration=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setting User Options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is possible to set user options that override the pandapower default parameters for one specific network. For the example network, the voltage angles are not calculated by default:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net)\n",
    "net.res_bus"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now set the option calculate_voltage_angles to True with the set_user_pf_options function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "set_user_pf_options(net, calculate_voltage_angles=True, init=\"dc\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we run another power flow without specifing parameters, the voltage angles are calculated:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net)\n",
    "net.res_bus"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This change in standard behaviour is only valid for this one network.\n",
    "\n",
    "When a parameter is specified directly in the runpp function, it overrides the user option:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net, calculate_voltage_angles=False)\n",
    "net.res_bus"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The hierarchy for power flow options is therefore:\n",
    "    1. Arguments passed to runpp\n",
    "    2. User Options\n",
    "    3. runpp default parameters\n",
    "Note however that there is a small exception for this rule that you have to deal with: When setting your own user options and then trying to override them with the same value as the runpp default parameter, this will not be recongnized within the powerflow and the user option value is used."
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
